package genindex

import (
	"fmt"
	"markdown_gen_index/src/util"
	"os"
	"path"
	"slices"
	"strings"
)

// var defaultGenerationOption = &GenerationOption{
// 	Override:  false,
// 	Recursive: true,
// 	Nav:       false,
// }

func (idxOpt *IndexOption) RootExcludes() []string {
	if idxOpt.rootExcludes == nil {
		idxOpt.rootExcludes = &[]string{}
		// .mdiignore
		*idxOpt.rootExcludes = append(*idxOpt.rootExcludes, getIgnoreEntry(path.Join(idxOpt.WorkDir, ".mdignore"))...)

		// .gitignore
		if idxOpt.InheritGitIgnore {
			*idxOpt.rootExcludes = append(*idxOpt.rootExcludes, getIgnoreEntry(path.Join(idxOpt.WorkDir, ".gitignore"))...)
		}
	}

	// fmt.Printf("(*idxOpt.rootExcludes): %v\n", (*idxOpt.rootExcludes))

	return *idxOpt.rootExcludes
}

func NewIndex(idxOpt *IndexOption) *index {
	files, err := os.ReadDir(idxOpt.WorkDir)
	if err != nil {
		panic(err)
	}

	// validate opt
	// if idxOpt == nil {
	// 	idxOpt = defaultIndexOption
	// }

	if idxOpt.WorkDir == "" {
		idxOpt.WorkDir = defaultIndexOption.WorkDir
	}
	if fi, err := os.Stat(idxOpt.WorkDir); os.IsNotExist(err) && !fi.IsDir() {
		panic(fmt.Sprintf("invalid work dir: %s", idxOpt.WorkDir))
	}
	if idxOpt.IndexTitle == "" {
		idxOpt.IndexTitle = defaultIndexOption.IndexTitle
	}
	if idxOpt.HomeTitle == "" {
		idxOpt.HomeTitle = idxOpt.IndexTitle
	}

	// if idxOpt.SubIndexFile == "" {
	// 	idxOpt.SubIndexFile = path.Join(idxOpt.WorkDir, defaultIndexFile)
	// }

	idx := &index{
		workDir:   idxOpt.WorkDir,
		file:      util.If(len(idxOpt.RootIndexFile) > 0, idxOpt.RootIndexFile, idxOpt.SubIndexFile),
		title:     idxOpt.IndexTitle,
		homeTitle: idxOpt.HomeTitle,
		children:  make([]*index, 0),
		entries:   make([]*entry, 0),
	}
	// set self as chain tail
	idx.chains = append(idxOpt.chains, idx)

	for _, f := range files {
		subFile := path.Join(idxOpt.WorkDir, f.Name())
		if matchFile(idxOpt.RootExcludes(), subFile) {
			continue
		}

		if f.IsDir() {
			if hasMdFile(subFile, idxOpt.SubIndexFile) {
				indexFile := path.Join(subFile, path.Base(idxOpt.SubIndexFile))
				subIndexOpt := &IndexOption{
					WorkDir:      subFile,
					IndexTitle:   readTitle(indexFile),
					HomeTitle:    idxOpt.HomeTitle,
					SubIndexFile: indexFile,
					rootExcludes: idxOpt.rootExcludes,
					subExcludes:  getSubExcludes(subFile),
					chains:       append(idxOpt.chains, idx), // append chains in sub index option
				}
				subIdx := NewIndex(subIndexOpt)
				if subIdx != nil {
					idx.children = append(idx.children, subIdx)
				}
			}
		} else {
			if slices.Contains(mdExts, path.Ext(f.Name())) && f.Name() != path.Base(idx.file) {
				idx.entries = append(idx.entries, &entry{
					title: readTitle(subFile),
					file:  subFile,
				})
			}
		}
	}

	for i := 0; i < len(idx.entries); i++ {
		if i > 0 {
			idx.entries[i].prev = idx.entries[i-1]
		}
		if i < len(idx.entries)-1 {
			idx.entries[i].next = idx.entries[i+1]
		}
	}

	return idx
}

func (idx *index) Generate(genOpt *GenerationOption) {
	if idx == nil {
		return
	}

	for _, subIdx := range idx.children {
		if genOpt.Recursive {
			subIdx.Generate(genOpt)
		}
	}
	content := parseContent(idx, &parseContentOption{
		WorkDir:      idx.workDir,
		Content:      "",
		NoHeaderLink: genOpt.NoHeaderLink,
	})

	if genOpt.Override {
		err := os.WriteFile(idx.file, []byte(fmt.Sprintf("%s# %s\n%s", idx.getIndexNav(), idx.title, content)), 0644)
		if err != nil {
			fmt.Printf("ERROR: failed to write index file: %s", err)
		} else {
			if genOpt.Verbose {
				fmt.Printf("OK: generated index file: %s\n", idx.file)
			}
		}
	} else {
		if genOpt.Verbose {
			fmt.Printf("SKIP: index file conflict: %s, use --override=true to override it\n", idx.file)
		}
	}

	if genOpt.Nav {
		idx.decorateEntry()
	}
}

func Clean(workDir, indexFile string) {
	if workDir == "" {
		workDir = "."
	}
	files, err := os.ReadDir(workDir)
	if err != nil {
		panic(err)
	}

	for _, f := range files {
		if f.IsDir() {
			Clean(path.Join(workDir, f.Name()), indexFile)
		} else {
			if f.Name() == indexFile {
				os.Remove(path.Join(workDir, f.Name()))
			}

			if slices.Contains(mdExts, path.Ext(f.Name())) {
				b, err := os.ReadFile(path.Join(workDir, f.Name()))
				if err == nil {
					if len(b) == 0 {
						continue
					}

					lines := strings.Split(string(b), "\n")

					if len(lines) > 4 {
						if lines[len(lines)-3] == "---" {
							lines = lines[:len(lines)-3]
						}
						if lines[len(lines)-5] == "---" {
							lines = lines[:len(lines)-5]
						}
					}
					if len(lines) > 1 {
						if strings.HasPrefix(lines[0], "[") && lines[1] == "" {
							lines = lines[2:]
						}
					}

					updated := strings.Join(lines, "\n")
					updatedFile, err := os.Create(path.Join(workDir, f.Name()))
					if err == nil {
						defer updatedFile.Close()
						updatedFile.WriteString(updated)
					}
				}
			}
		}
	}
}
